/*
 * Copyright (C) 2013 Peng fei Pan <sky@xiaopan.me>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package me.xiaopan.android.net;

import android.annotation.TargetApi;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.telephony.TelephonyManager;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import me.xiaopan.java.lang.reflect.ReflectUtils;

/**
 * 网络工具箱
 */
public class NetworkUtils {
	/**
	 * 网络类型 - 无连接
	 */
	public static final int NETWORK_TYPE_NO_CONNECTION = -1231545315;
	
	/**
	 * 获取当前网络的状态
	 * @param context 上下文
	 * @return 当前网络的状态。具体类型可参照NetworkInfo.State.CONNECTED、NetworkInfo.State.CONNECTED.DISCONNECTED等字段。当前没有网络连接时返回null
	 */
	public static NetworkInfo.State getCurrentNetworkState(Context context){
		NetworkInfo networkInfo = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
		return networkInfo != null?networkInfo.getState():null;
	}
	
	/**
	 * 获取当前网络的类型
	 * @param context 上下文
	 * @return 当前网络的类型。具体类型可参照ConnectivityManager中的TYPE_BLUETOOTH、TYPE_MOBILE、TYPE_WIFI等字段。当前没有网络连接时返回NetworkUtils.NETWORK_TYPE_NO_CONNECTION
	 */
	public static int getCurrentNetworkType(Context context){
		NetworkInfo networkInfo = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
		return networkInfo != null?networkInfo.getType():NETWORK_TYPE_NO_CONNECTION;
	}
	
	/**
	 * 获取当前网络的具体类型
	 * @param context 上下文
	 * @return 当前网络的具体类型。具体类型可参照TelephonyManager中的NETWORK_TYPE_1xRTT、NETWORK_TYPE_CDMA等字段。当前没有网络连接时返回NetworkUtils.NETWORK_TYPE_NO_CONNECTION
	 */
	public static int getCurrentNetworkSubtype(Context context){
		NetworkInfo networkInfo = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
		return networkInfo != null?networkInfo.getSubtype():NETWORK_TYPE_NO_CONNECTION;
	}
	
	/**
	 * 判断当前网络是否已经连接
	 * @param context 上下文
	 * @return 当前网络是否已经连接。false：尚未连接
	 */
	public static boolean isConnectedByState(Context context){
		return getCurrentNetworkState(context) == NetworkInfo.State.CONNECTED;
	}
	
	/**
	 * 判断当前网络是否正在连接
	 * @param context 上下文
	 * @return 当前网络是否正在连接
	 */
	public static boolean isConnectingByState(Context context){
		return getCurrentNetworkState(context) == NetworkInfo.State.CONNECTING;
	}
	
	/**
	 * 判断当前网络是否已经断开
	 * @param context 上下文
	 * @return 当前网络是否已经断开
	 */
	public static boolean isDisconnectedByState(Context context){
		return getCurrentNetworkState(context) == NetworkInfo.State.DISCONNECTED;
	}
	
	/**
	 * 判断当前网络是否正在断开
	 * @param context 上下文
	 * @return 当前网络是否正在断开
	 */
	public static boolean isDisconnectingByState(Context context){
		return getCurrentNetworkState(context) == NetworkInfo.State.DISCONNECTING;
	}
	
	/**
	 * 判断当前网络是否已经暂停
	 * @param context 上下文
	 * @return 当前网络是否已经暂停
	 */
	public static boolean isSuspendedByState(Context context){
		return getCurrentNetworkState(context) == NetworkInfo.State.SUSPENDED;
	}
	
	/**
	 * 判断当前网络是否处于未知状态中
	 * @param context 上下文
	 * @return 当前网络是否处于未知状态中
	 */
	public static boolean isUnknownByState(Context context){
		return getCurrentNetworkState(context) == NetworkInfo.State.UNKNOWN;
	}
	
	/**
	 * 判断当前网络的类型是否是蓝牙
	 * @param context 上下文
	 * @return 当前网络的类型是否是蓝牙。false：当前没有网络连接或者网络类型不是蓝牙
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
	public static boolean isBluetoothByType(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2){
			return false;
		}else{
			return getCurrentNetworkType(context) == ConnectivityManager.TYPE_BLUETOOTH;
		}
	}
	
	/**
	 * 判断当前网络的类型是否是虚拟网络
	 * @param context 上下文
	 * @return 当前网络的类型是否是虚拟网络。false：当前没有网络连接或者网络类型不是虚拟网络
	 */
	@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
	public static boolean isDummyByType(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2){
			return false;
		}else{
			return getCurrentNetworkType(context) == ConnectivityManager.TYPE_DUMMY;
		}
	}
	
	/**
	 * 判断当前网络的类型是否是ETHERNET
	 * @param context 上下文
	 * @return 当前网络的类型是否是ETHERNET。false：当前没有网络连接或者网络类型不是ETHERNET
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
	public static boolean isEthernetByType(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2){
			return false;
		}else{
			return getCurrentNetworkType(context) == ConnectivityManager.TYPE_ETHERNET;
		}
	}
	
	/**
	 * 判断当前网络的类型是否是移动网络
	 * @param context 上下文
	 * @return 当前网络的类型是否是移动网络。false：当前没有网络连接或者网络类型不是移动网络
	 */
	public static boolean isMobileByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_MOBILE;
	}
	
	/**
	 * 判断当前网络的类型是否是MobileDun
	 * @param context 上下文
	 * @return 当前网络的类型是否是MobileDun。false：当前没有网络连接或者网络类型不是MobileDun
	 */
	public static boolean isMobileDunByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_MOBILE_DUN;
	}
	
	/**
	 * 判断当前网络的类型是否是MobileHipri
	 * @param context 上下文
	 * @return 当前网络的类型是否是MobileHipri。false：当前没有网络连接或者网络类型不是MobileHipri
	 */
	public static boolean isMobileHipriByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_MOBILE_HIPRI;
	}
	
	/**
	 * 判断当前网络的类型是否是MobileMms
	 * @param context 上下文
	 * @return 当前网络的类型是否是MobileMms。false：当前没有网络连接或者网络类型不是MobileMms
	 */
	public static boolean isMobileMmsByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_MOBILE_MMS;
	}
	
	/**
	 * 判断当前网络的类型是否是MobileSupl
	 * @param context 上下文
	 * @return 当前网络的类型是否是MobileSupl。false：当前没有网络连接或者网络类型不是MobileSupl
	 */
	public static boolean isMobileSuplByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_MOBILE_SUPL;
	}
	
	/**
	 * 判断当前网络的类型是否是Wifi
	 * @param context 上下文
	 * @return 当前网络的类型是否是Wifi。false：当前没有网络连接或者网络类型不是wifi
	 */
	public static boolean isWifiByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_WIFI;
	}
	
	/**
	 * 判断当前网络的类型是否是Wimax
	 * @param context 上下文
	 * @return 当前网络的类型是否是Wimax。false：当前没有网络连接或者网络类型不是Wimax
	 */
	public static boolean isWimaxByType(Context context){
		return getCurrentNetworkType(context) == ConnectivityManager.TYPE_WIMAX;
	}
	
	/**
	 * 判断当前网络的具体类型是否是1XRTT
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是1XRTT。false：当前没有网络连接或者具体类型不是1XRTT
	 */
	public static boolean is1XRTTBySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_1xRTT;
	}
	
	/**
	 * 判断当前网络的具体类型是否是CDMA（Either IS95A or IS95B）
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是CDMA。false：当前没有网络连接或者具体类型不是CDMA
	 */
	public static boolean isCDMABySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_CDMA;
	}
	
	/**
	 * 判断当前网络的具体类型是否是EDGE
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是EDGE。false：当前没有网络连接或者具体类型不是EDGE
	 */
	public static boolean isEDGEBySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_EDGE;
	}
	
	/**
	 * 判断当前网络的具体类型是否是EHRPD
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是EHRPD。false：当前没有网络连接或者具体类型不是EHRPD
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	public static boolean isEHRPDBySubtype(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
			return false;
		}else{
			return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_EHRPD;
		}
	}
	
	/**
	 * 判断当前网络的具体类型是否是EVDO_0
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是EVDO_0。false：当前没有网络连接或者具体类型不是EVDO_0
	 */
	public static boolean isEVDO_0BySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_EVDO_0;
	}
	
	/**
	 * 判断当前网络的具体类型是否是EVDO_A
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是EVDO_A。false：当前没有网络连接或者具体类型不是EVDO_A
	 */
	public static boolean isEVDO_ABySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_EVDO_A;
	}
	
	/**
	 * 判断当前网络的具体类型是否是EDGE
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是EVDO_B。false：当前没有网络连接或者具体类型不是EVDO_B
	 */
	@TargetApi(Build.VERSION_CODES.GINGERBREAD)
	public static boolean isEVDO_BBySubtype(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD){
			return false;
		}else{
			return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_EVDO_B;
		}
	}
	
	/**
	 * 判断当前网络的具体类型是否是GPRS
	 * EVDO_Bam context 上下文
	 * @return false：当前网络的具体类型是否是GPRS。false：当前没有网络连接或者具体类型不是GPRS
	 */
	public static boolean isGPRSBySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_GPRS;
	}
	
	/**
	 * 判断当前网络的具体类型是否是HSDPA
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是HSDPA。false：当前没有网络连接或者具体类型不是HSDPA
	 */
	public static boolean isHSDPABySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_HSDPA;
	}
	
	/**
	 * 判断当前网络的具体类型是否是HSPA
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是HSPA。false：当前没有网络连接或者具体类型不是HSPA
	 */
	public static boolean isHSPABySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_HSPA;
	}
	
	/**
	 * 判断当前网络的具体类型是否是HSPAP
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是HSPAP。false：当前没有网络连接或者具体类型不是HSPAP
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
	public static boolean isHSPAPBySubtype(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2){
			return false;
		}else{
			return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_HSPAP;
		}
	}
	
	/**
	 * 判断当前网络的具体类型是否是HSUPA
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是HSUPA。false：当前没有网络连接或者具体类型不是HSUPA
	 */
	public static boolean isHSUPABySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_HSUPA;
	}
	
	/**
	 * 判断当前网络的具体类型是否是IDEN
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是IDEN。false：当前没有网络连接或者具体类型不是IDEN
	 */
	public static boolean isIDENBySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_IDEN;
	}
	
	/**
	 * 判断当前网络的具体类型是否是LTE
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是LTE。false：当前没有网络连接或者具体类型不是LTE
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	public static boolean isLTEBySubtype(Context context){
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
			return false;
		}else{
			return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_LTE;
		}
	}
	
	/**
	 * 判断当前网络的具体类型是否是UMTS
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是UMTS。false：当前没有网络连接或者具体类型不是UMTS
	 */
	public static boolean isUMTSBySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_UMTS;
	}
	
	/**
	 * 判断当前网络的具体类型是否是UNKNOWN
	 * @param context 上下文
	 * @return false：当前网络的具体类型是否是UNKNOWN。false：当前没有网络连接或者具体类型不是UNKNOWN
	 */
	public static boolean isUNKNOWNBySubtype(Context context){
		return getCurrentNetworkSubtype(context) == TelephonyManager.NETWORK_TYPE_UNKNOWN;
	}
	
	/**
	 * 判断当前网络是否是中国移动2G网络
	 * @param context 上下文
	 * @return false：不是中国移动2G网络或者当前没有网络连接
	 */
	public static boolean isChinaMobile2G(Context context){
		return isEDGEBySubtype(context);
	}
	
	/**
	 * 判断当前网络是否是中国联通2G网络
	 * @param context 上下文
	 * @return false：不是中国联通2G网络或者当前没有网络连接
	 */
	public static boolean isChinaUnicom2G(Context context){
		return isGPRSBySubtype(context);
	}
	
	/**
	 * 判断当前网络是否是中国联通3G网络
	 * @param context 上下文
	 * @return false：不是中国联通3G网络或者当前没有网络连接
	 */
	public static boolean isChinaUnicom3G(Context context){
		return isHSDPABySubtype(context) || isUMTSBySubtype(context);
	}
	
	/**
	 * 判断当前网络是否是中国电信2G网络
	 * @param context 上下文
	 * @return false：不是中国电信2G网络或者当前没有网络连接
	 */
	public static boolean isChinaTelecom2G(Context context){
		return isCDMABySubtype(context);
	}
	
	/**
	 * 判断当前网络是否是中国电信3G网络
	 * @param context 上下文
	 * @return false：不是中国电信3G网络或者当前没有网络连接
	 */
	public static boolean isChinaTelecom3G(Context context){
		return isEVDO_0BySubtype(context) || isEVDO_ABySubtype(context) || isEVDO_BBySubtype(context);
	}
	
	/**
	 * 获取Wifi的状态，需要ACCESS_WIFI_STATE权限
	 * @param context 上下文
	 * @return 取值为WifiManager中的WIFI_STATE_ENABLED、WIFI_STATE_ENABLING、WIFI_STATE_DISABLED、WIFI_STATE_DISABLING、WIFI_STATE_UNKNOWN之一
	 * @throws Exception 没有找到wifi设备
	 */
	public static int getWifiState(Context context) throws Exception{
		WifiManager wifiManager = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE));
		if(wifiManager != null){
			return wifiManager.getWifiState();
		}else{
			throw new Exception("wifi device not found!");
		}
	}
	
	/**
	 * 判断Wifi是否打开，需要ACCESS_WIFI_STATE权限
	 * @param context 上下文
	 * @return true：打开；false：关闭
	 */
	public static boolean isWifiOpen(Context context) throws Exception{
		int wifiState = getWifiState(context);
		return wifiState == WifiManager.WIFI_STATE_ENABLED || wifiState == WifiManager.WIFI_STATE_ENABLING ? true : false;
	}
	
	/**
	 * 设置Wifi，需要CHANGE_WIFI_STATE权限
	 * @param context 上下文
	 * @param enable wifi状态
	 * @return 设置是否成功
	 */
	public static boolean setWifi(Context context, boolean enable) throws Exception{
		//如果当前wifi的状态和要设置的状态不一样
		if(isWifiOpen(context) != enable){
			((WifiManager) context.getSystemService(Context.WIFI_SERVICE)).setWifiEnabled(enable);
		}
		return true;
	}
	
	/**
	 * 判断移动网络是否打开，需要ACCESS_NETWORK_STATE权限
	 * @param context 上下文
	 * @return true：打开；false：关闭
	 */
	public static boolean isMobileNetworkOpen(Context context){
		return (((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getNetworkInfo(ConnectivityManager.TYPE_MOBILE)).isConnected();
	}
	
	/**
	 * 设置移动网络，需要CHANGE_NETWORK_STATE权限
	 * @param context
	 * @param eanble 状态
	 * @return true：设置成功；false：设置失败
	 */
	public static boolean setMobileNetwork(Context context, boolean eanble){ 
		boolean result = false;
		ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		Method method = ReflectUtils.getMethod(mConnectivityManager.getClass(), "setMobileDataEnabled", boolean.class);
		try {
			if(method != null){
				method.invoke(mConnectivityManager, eanble);
				result = true;
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 获取本机IP地址
	 * @return null：没有网络连接
	 */
	public static String getIpAddress() {
		try {
			NetworkInterface nerworkInterface;
			InetAddress inetAddress;
			for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
				nerworkInterface = en.nextElement();
				for (Enumeration<InetAddress> enumIpAddr = nerworkInterface.getInetAddresses(); enumIpAddr.hasMoreElements();) {
					inetAddress = enumIpAddr.nextElement();
					if (!inetAddress.isLoopbackAddress()) {
						return inetAddress.getHostAddress().toString();
					}
				}
			}
			return null;
		} catch (SocketException ex) {
			ex.printStackTrace();
			return null;
		}
	}
}
